using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace DocumentCreationSystem.Services
{
    /// <summary>
    /// 补丁服务：生成与应用统一 diff（简化版，与本项目生成器兼容）
    /// </summary>
    public interface IPatchService
    {
        string CreateUnifiedDiff(string filePath, string oldText, string newText);
        Task<string> CreatePatchAsync(string filePath, string newContentBase64);
        Task<string> ApplyPatchFromBase64Async(string patchBase64, string? allowedRootDir);
    }

    public class PatchService : IPatchService
    {
        private readonly ILogger<PatchService> _logger;
        public PatchService(ILogger<PatchService> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 生成统一 diff（与 CodeIntelligenceService 中的 CreateUnifiedDiff 兼容的简单实现）
        /// </summary>
        public string CreateUnifiedDiff(string filePath, string oldText, string newText)
        {
            var oldLines = NormalizeLines(oldText);
            var newLines = NormalizeLines(newText);
            if (oldText == newText) return string.Empty;
            var sb = new StringBuilder();
            sb.AppendLine($"--- a/{filePath}");
            sb.AppendLine($"+++ b/{filePath}");
            int i = 0, j = 0;
            while (i < oldLines.Length || j < newLines.Length)
            {
                if (i < oldLines.Length && j < newLines.Length && oldLines[i] == newLines[j])
                {
                    i++; j++; continue;
                }
                int startI = i, startJ = j;
                var hunk = new List<string>();
                while (i < oldLines.Length && (j >= newLines.Length || oldLines[i] != newLines[j]))
                {
                    hunk.Add("-" + oldLines[i]);
                    i++;
                }
                while (j < newLines.Length && (i >= oldLines.Length || oldLines[i] != newLines[j]))
                {
                    hunk.Add("+" + newLines[j]);
                    j++;
                }
                sb.AppendLine($"@@ -{startI + 1},{Math.Max(0, i - startI)} +{startJ + 1},{Math.Max(0, j - startJ)} @@");
                foreach (var l in hunk) sb.AppendLine(l);
            }
            return sb.ToString();
        }

        public async Task<string> CreatePatchAsync(string filePath, string newContentBase64)
        {
            try
            {
                if (!File.Exists(filePath)) return $"❌ 文件不存在: {filePath}";
                var oldText = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
                var newBytes = Convert.FromBase64String(newContentBase64);
                var newText = Encoding.UTF8.GetString(newBytes);
                var diff = CreateUnifiedDiff(filePath, oldText, newText);
                return string.IsNullOrEmpty(diff) ? "(无变更)" : diff;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"创建补丁失败: {filePath}");
                return $"❌ 创建补丁失败: {ex.Message}";
            }
        }

        /// <summary>
        /// 应用 Base64 的统一 diff 补丁（仅支持本项目生成的简化 diff）。
        /// 为安全起见，只允许在 allowedRootDir 范围内修改。
        /// </summary>
        public async Task<string> ApplyPatchFromBase64Async(string patchBase64, string? allowedRootDir)
        {
            try
            {
                var patchText = Encoding.UTF8.GetString(Convert.FromBase64String(patchBase64));
                var changes = ParseUnifiedDiff(patchText);
                if (changes.Count == 0) return "(无可应用变更)";
                int appliedFiles = 0, appliedHunks = 0;
                foreach (var change in changes)
                {
                    var path = RemovePrefix(change.FilePath);
                    if (Path.IsPathRooted(path))
                    {
                        // 绝对路径直接校验
                        if (!IsUnderRoot(path, allowedRootDir))
                            return $"❌ 拒绝修改，路径超出允许范围: {path}";
                    }
                    else if (!string.IsNullOrEmpty(allowedRootDir))
                    {
                        path = Path.GetFullPath(Path.Combine(allowedRootDir!, path));
                        if (!IsUnderRoot(path, allowedRootDir)) return $"❌ 拒绝修改，路径超出允许范围: {path}";
                    }

                    var lines = File.Exists(path) ? (await File.ReadAllTextAsync(path)).Replace("\r\n","\n").Split('\n').ToList() : new List<string>();
                    int lineDelta = 0;
                    foreach (var h in change.Hunks)
                    {
                        var idx = h.OldStart - 1 + lineDelta;
                        if (idx < 0 || idx > lines.Count) return $"❌ 补丁失配: 文件 {path} 行索引异常";
                        // 删除旧行
                        if (h.OldLength > 0)
                        {
                            if (idx + h.OldLength > lines.Count) return $"❌ 补丁失配: 文件 {path} 旧行范围越界";
                            lines.RemoveRange(idx, h.OldLength);
                            lineDelta -= h.OldLength;
                        }
                        // 插入新行
                        if (h.NewLines.Count > 0)
                        {
                            lines.InsertRange(idx, h.NewLines);
                            lineDelta += h.NewLines.Count;
                        }
                        appliedHunks++;
                    }
                    var finalText = string.Join("\n", lines);
                    // 写入时统一为 UTF-8
                    await File.WriteAllTextAsync(path, finalText, new UTF8Encoding(false));
                    appliedFiles++;
                }
                return $"✅ 已应用补丁：{appliedFiles} 个文件，{appliedHunks} 个区块";
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "应用补丁失败");
                return $"❌ 应用补丁失败: {ex.Message}";
            }
        }

        private static string[] NormalizeLines(string text)
        {
            return text.Replace("\r\n", "\n").Split('\n');
        }

        private static bool IsUnderRoot(string path, string? root)
        {
            if (string.IsNullOrEmpty(root)) return true;
            var normPath = Path.GetFullPath(path);
            var normRoot = Path.GetFullPath(root);
            return normPath.StartsWith(normRoot, StringComparison.OrdinalIgnoreCase);
        }

        private static string RemovePrefix(string s)
        {
            if (s.StartsWith("a/")) return s.Substring(2);
            if (s.StartsWith("b/")) return s.Substring(2);
            return s;
        }

        private sealed class FileChange
        {
            public string FilePath { get; set; } = string.Empty;
            public List<Hunk> Hunks { get; } = new();
        }

        private sealed class Hunk
        {
            public int OldStart { get; set; }
            public int OldLength { get; set; }
            public int NewStart { get; set; }
            public int NewLength { get; set; }
            public List<string> NewLines { get; } = new();
        }

        private static List<FileChange> ParseUnifiedDiff(string diff)
        {
            var result = new List<FileChange>();
            FileChange? current = null;
            using var reader = new StringReader(diff);
            string? line;
            while ((line = reader.ReadLine()) != null)
            {
                if (line.StartsWith("--- "))
                {
                    // 跳过旧文件标记
                    continue;
                }
                if (line.StartsWith("+++ "))
                {
                    var path = line.Substring(4).Trim(); // 形如 b/xxx
                    current = new FileChange { FilePath = path.StartsWith("b/") ? path.Substring(2) : path };
                    result.Add(current);
                    continue;
                }
                if (line.StartsWith("@@ "))
                {
                    if (current == null) continue;
                    // @@ -oldStart,oldLen +newStart,newLen @@
                    var seg = line.Trim('@',' ').Split(' ');
                    var oldPart = seg.First(s => s.StartsWith("-"));
                    var newPart = seg.First(s => s.StartsWith("+"));
                    var oldVals = oldPart.Substring(1).Split(',');
                    var newVals = newPart.Substring(1).Split(',');
                    var h = new Hunk
                    {
                        OldStart = int.Parse(oldVals[0]),
                        OldLength = oldVals.Length > 1 ? int.Parse(oldVals[1]) : 0,
                        NewStart = int.Parse(newVals[0]),
                        NewLength = newVals.Length > 1 ? int.Parse(newVals[1]) : 0,
                    };
                    current.Hunks.Add(h);
                    // 读取后续 + / - 行，只记录新增行供插入
                    while ((line = reader.ReadLine()) != null && (line.StartsWith("+") || line.StartsWith("-")))
                    {
                        if (line.StartsWith("+")) h.NewLines.Add(line.Substring(1));
                        // '-' 行代表被删除的旧行，应用时按 OldLength 批量删除，不需记录内容
                    }
                    // 继续处理下一行（可能为 @@ 或 +++ 或 EOF），因此回退一行逻辑：使用外层 while 的当前 line
                    if (line == null) break; // EOF
                    // 否则 line 已是下一轮 while 的起点
                }
            }
            return result;
        }
    }
}

